package cn.bandeqing.product.rpc.impl;

import cn.bandeqing.attachment.entity.AttachmentType;
import cn.bandeqing.attachment.resp.AttachmentDetail;
import cn.bandeqing.attachment.service.IAttachmentService;
import cn.bandeqing.base.resp.Resp;
import cn.bandeqing.base.resp.Table;
import cn.bandeqing.finance.resp.LabelValue;
import cn.bandeqing.frame.exception.CodeException;
import cn.bandeqing.orderEvaluate.service.IOrderEvaluateService;
import cn.bandeqing.product.entity.Product;
import cn.bandeqing.product.entity.ProductStatus;
import cn.bandeqing.product.req.*;
import cn.bandeqing.product.resp.*;
import cn.bandeqing.product.rpc.IProductRpc;
import cn.bandeqing.product.service.IProductService;
import cn.bandeqing.productInventory.entity.ProductInventory;
import cn.bandeqing.productInventory.service.IProductInventoryService;
import cn.bandeqing.productInventoryRecord.entity.ChangeType;
import cn.bandeqing.productInventoryRecord.entity.OperateType;
import cn.bandeqing.productInventoryRecord.entity.ProductInventoryRecord;
import cn.bandeqing.productInventoryRecord.service.IProductInventoryRecordService;
import cn.bandeqing.productSpecification.entity.ProductSpecification;
import cn.bandeqing.productSpecification.service.IProductSpecificationService;
import cn.bandeqing.store.entity.Store;
import cn.bandeqing.store.service.IStoreService;
import cn.bandeqing.store.service.impl.StoreServiceImpl;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import io.github.linpeilie.Converter;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.RandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static cn.bandeqing.attachment.entity.table.AttachmentTableDef.ATTACHMENT;
import static cn.bandeqing.orderEvaluate.entity.table.OrderEvaluateTableDef.ORDER_EVALUATE;
import static cn.bandeqing.product.entity.table.ProductTableDef.PRODUCT;
import static cn.bandeqing.productInventory.entity.table.ProductInventoryTableDef.PRODUCT_INVENTORY;
import static cn.bandeqing.productInventoryRecord.entity.table.ProductInventoryRecordTableDef.PRODUCT_INVENTORY_RECORD;
import static cn.bandeqing.productSpecification.entity.table.ProductSpecificationTableDef.PRODUCT_SPECIFICATION;

@DubboService
public class ProductRpcImpl implements IProductRpc {

    @Resource
    private IProductService productService;
    @Resource
    private IProductSpecificationService productSpecificationService;
    @Resource
    private IProductInventoryService productInventoryService;
    @Resource
    private IProductInventoryRecordService productInventoryRecordService;
    @Resource
    private IAttachmentService attachmentService;
    @Resource
    private IOrderEvaluateService orderEvaluateService;
    @Resource
    private IStoreService storeService;
    @Resource
    private Converter converter;

    @Override
    public Resp<Table<ProductTable>> table(ProductTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(PRODUCT.PRODUCT_ID.eq(req.getProductId()))
                .where(PRODUCT.PRODUCT_UUID.eq(req.getProductUuid()))
                .where(PRODUCT.PRODUCT_NAME.eq(req.getProductName()))
                .where(PRODUCT.PRODUCT_STATUS.eq(req.getProductStatus()))
                .where(PRODUCT.PRODUCT_TYPE_ID.eq(req.getProductTypeId()))
                .orderBy(PRODUCT.PRODUCT_ID.desc());

        Page<ProductTable> page = productService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, ProductTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    public void saveOrUpdate(ProductSaveOrUpdateReq req) {
        Product product = converter.convert(req, Product.class);
        if (product.getProductId() == null) {
            product.setProductUuid(RandomUtil.randomNumbers(8));
        }
        boolean b = productService.saveOrUpdate(product);
        Assert.isTrue(b, () -> new CodeException("productService.saveOrUpdate(pt)"));
    }

    @Override
    public Resp<Table<ProductSpecificationTable>> productSpecificationTable(ProductSpecificationTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(PRODUCT_SPECIFICATION.PRODUCT_SPECIFICATION_ID.eq(req.getProductSpecificationId()))
                .where(PRODUCT_SPECIFICATION.PRODUCT_SPECIFICATION_.eq(req.getProductSpecification()))
                .where(PRODUCT_SPECIFICATION.PRODUCT_PRICE.eq(req.getProductPrice()))
                .where(PRODUCT_SPECIFICATION.PRODUCT_ID.eq(req.getProductId()))
                .orderBy(PRODUCT_SPECIFICATION.PRODUCT_SPECIFICATION_ID.desc());

        Page<ProductSpecificationTable> page = productSpecificationService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, ProductSpecificationTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    public void productSpecificationSaveOrUpdate(ProductSpecificationSaveOrUpdateReq req) {
        ProductSpecification ps = converter.convert(req, ProductSpecification.class);
        boolean b = productSpecificationService.saveOrUpdate(ps);
        Assert.isTrue(b, CodeException::new);
    }

    @Override
    public Resp<Table<ProductInventoryTable>> productInventoryTable(ProductInventoryTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(PRODUCT_INVENTORY.PRODUCT_ID.eq(req.getProductId()))
                .where(PRODUCT_INVENTORY.PRODUCT_SPECIFICATION_ID.eq(req.getProductSpecificationId()))
                .where(PRODUCT_INVENTORY.STORE_ID.eq(req.getStoreId()))
                .orderBy(PRODUCT_INVENTORY.INVENTORY_ID.desc());

        Page<ProductInventoryTable> page = productInventoryService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, ProductInventoryTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    @Transactional
    public void productInventorySaveOrUpdate(ProductInventorySaveOrUpdateReq req) {
        ProductInventory pi = converter.convert(req, ProductInventory.class);
        boolean b = productInventoryService.saveOrUpdate(pi);
        Assert.isTrue(b, CodeException::new);

        ProductInventoryRecord pir = converter.convert(req, ProductInventoryRecord.class);
//        pir.setOperateType(OperateType.enter);
        pir.setChangeInventory(req.getProductInventory());
        pir.setRemark("初始化库存");
        pir.setInventoryId(pi.getInventoryId());
        pir.setAfterInventory(req.getProductInventory());
        pir.setBeforeInventory(0);
        pir.setChangeType(ChangeType.product_init);
        boolean save = productInventoryRecordService.save(pir);
        Assert.isTrue(save, CodeException::new);

    }

    @Override
    public Resp<List<LabelValue>> specificationSelect(ProductSpecificationReq req) {
        List<LabelValue> labelValues = productSpecificationService.listAs(QueryWrapper.create()
                .select(PRODUCT_SPECIFICATION.PRODUCT_SPECIFICATION_.as(LabelValue::getLabel),
                        PRODUCT_SPECIFICATION.PRODUCT_SPECIFICATION_ID.as(LabelValue::getValue))
                .where(PRODUCT_SPECIFICATION.PRODUCT_ID.eq(req.getProductId())), LabelValue.class);
        return Resp.success(labelValues);
    }

    @Override
    @Transactional
    public void productChanegInventory(ProductChangeInventoryReq req) {
        ProductInventory pi = productInventoryService.getById(req.getInventoryId());
        Integer before = pi.getProductInventory();
        int after = 0;

        ProductInventoryRecord pir = converter.convert(req, ProductInventoryRecord.class);

        if (OperateType.out.equals(pir.getOperateType())) {
            // 出库
            after = before - req.getChangeInventory();
        } else if (OperateType.enter.equals(pir.getOperateType())) {
            // 入库
            after = before + req.getChangeInventory();
        }

        pir.setBeforeInventory(before);
        pir.setAfterInventory(after);
        pir.setChangeType(ChangeType.user_operation);

        boolean b = productInventoryService.updateById(ProductInventory.builder()
                .inventoryId(req.getInventoryId())
                .productInventory(after)
                .build());
        Assert.isTrue(b, CodeException::new);

        boolean save = productInventoryRecordService.save(pir);
        Assert.isTrue(save, CodeException::new);
    }

    @Override
    public Resp<Table<ProductInventoryRecordTable>> inventoryRecordTable(ProductInventoryRecordTableReq req) {
        QueryWrapper qw = QueryWrapper.create()
                .where(PRODUCT_INVENTORY_RECORD.INVENTORY_ID.eq(req.getInventoryId()))
                .where(PRODUCT_INVENTORY_RECORD.PRODUCT_ID.eq(req.getProductId()))
                .where(PRODUCT_INVENTORY_RECORD.PRODUCT_SPECIFICATION_ID.eq(req.getProductSpecificationId()))
                .where(PRODUCT_INVENTORY_RECORD.STORE_ID.eq(req.getStoreId()))
                .orderBy(PRODUCT_INVENTORY_RECORD.PRODUCT_INVENTORY_RECORD_ID.desc());

        Page<ProductInventoryRecordTable> page = productInventoryRecordService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, ProductInventoryRecordTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    public Resp<List<LabelValue>> select(ProductSelectReq req) {
        List<LabelValue> labelValues = productService.listAs(QueryWrapper.create()
                .select(PRODUCT.PRODUCT_ID.as(LabelValue::getValue),
                        PRODUCT.PRODUCT_NAME.as(LabelValue::getLabel)), LabelValue.class);
        return Resp.success(labelValues);
    }

    @Override
    public Resp<List<ProductListResp>> list(ProductListReq req) {
        List<ProductListResp> productListResps = productService.listAs(QueryWrapper.create()
                .where(PRODUCT.PRODUCT_TYPE_ID.eq(req.getProductTypeId()))
                .where(PRODUCT.PRODUCT_STATUS.eq(ProductStatus.ok))
                .limit((req.getPage() - 1) * req.getSize(), req.getSize()), ProductListResp.class);

        return Resp.success(productListResps);
    }

    @Override
    public Resp<ProductDetailResp> detail(ProductDetailReq req) {
        ProductDetailResp detailResp = productService.getOneAs(QueryWrapper.create()
                .where(PRODUCT.PRODUCT_ID.eq(req.getProductId())), ProductDetailResp.class);

        List<AttachmentDetail> bannerList = attachmentService.listAs(QueryWrapper.create()
                .where(ATTACHMENT.OBJ_TYPE.eq(AttachmentType.product_banner))
                .where(ATTACHMENT.OBJ_ID.eq(detailResp.getProductId())), AttachmentDetail.class);
        detailResp.setBannerList(bannerList);

        List<AttachmentDetail> detailList = attachmentService.listAs(QueryWrapper.create()
                .where(ATTACHMENT.OBJ_TYPE.eq(AttachmentType.product_detail))
                .where(ATTACHMENT.OBJ_ID.eq(detailResp.getProductId())), AttachmentDetail.class);
        detailResp.setDetailList(detailList);

        List<SpecificationDetail> specificationDetails = productSpecificationService.listAs(QueryWrapper.create()
                .where(PRODUCT_SPECIFICATION.PRODUCT_ID.eq(detailResp.getProductId())), SpecificationDetail.class);
        detailResp.setSpecificationList(specificationDetails);

        List<ProductEvaluateDetail> productEvaluateDetails = orderEvaluateService.listAs(QueryWrapper.create()
                .where(ORDER_EVALUATE.PRODUCT_ID.eq(detailResp.getProductId()))
                .limit(3), ProductEvaluateDetail.class);
        detailResp.setEvaluateList(productEvaluateDetails);

        return Resp.success(detailResp);
    }

    @Override
    public Resp<List<EvaluateDetail>> evaluateList(EvaluateListReq req) {

        List<EvaluateDetail> evaluateDetails = orderEvaluateService.listAs(QueryWrapper.create()
                .where(ORDER_EVALUATE.PRODUCT_ID.eq(req.getProductId()))
                .limit((req.getPage() - 1) * req.getSize(), req.getSize()), EvaluateDetail.class);

        return Resp.success(evaluateDetails);
    }

    @Override
    @Transactional
    public Resp<Void> refreshAllProductInventory() {

        // 所有规格
        List<ProductSpecification> productSpecifications = productSpecificationService.list();

        // 所有店铺
        List<Store> stores = storeService.list();

        List<ProductInventory> productInventories = productInventoryService.list();

        ArrayList<ProductInventory> saves = ListUtil.of();
        for (ProductSpecification ps : productSpecifications) {
            A:
            for (Store store : stores) {

                for (ProductInventory productInventory : productInventories) {
                    if (productInventory.getProductId().equals(ps.getProductId()) && productInventory.getStoreId().equals(store.getStoreId())) {
                        continue A;
                    }
                }

                saves.add(ProductInventory.builder()
                        .productId(ps.getProductId())
                        .productName(ps.getProductName())
                        .productSpecification(ps.getProductSpecification())
                        .productSpecificationId(ps.getProductSpecificationId())
                        .storeId(store.getStoreId())
                        .storeName(store.getStoreName())
                        .productInventory(0)
                        .build());
            }
        }

        productInventoryService.saveBatch(saves);

        return Resp.success();
    }

}
